/*

Copyright (C) 2008 Victor Manuel Sanchez Cartagena, Juan Antonio Perez Ortiz


This file is part of ANTLRArbol.

ANTLRArbol is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

ANTLRArbol is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. 

*/

grammar Java;
options {backtrack=true; memoize=true;}

@parser::header
{
  package org.ANTLRArbol.grammars.Java;
  import java.util.Set;
  import java.util.HashSet;
}

@lexer::header
{
  package org.ANTLRArbol.grammars.Java;
}

@lexer::members {
  protected boolean enumIsKeyword = true;
  protected boolean assertIsKeyword = true;
}

@parser::members
{
    private String traduccion="";
    public String getTraduccion()
    {
      return traduccion;
    }
    
    private int ambito=-1;
    public void setAmbito(int amb)
    {
      ambito=amb;
    }
    
    private ArrayList<Set<String>> listaExpresiones;
    public ArrayList<Set<String>> getListaExpresiones()
    {
        return listaExpresiones;
    }
    
    public final static int TIPO_SUFIJO_OTRO=0;
    public final static int TIPO_SUFIJO_ARRAY=1;
    public final static int TIPO_SUFIJO_METODO=2;
}

bloque
  :
  block[true]  {traduccion=$block.s;}
  ;
  
expresionesSepComas
:
  e=expressionList {listaExpresiones=$e.listaVars;}
;  
  
block [boolean primero] returns [String s]
@init
{
  $s="";
}
    :   '{' {ambito++;}  (b=blockStatement {$s+=$b.s;} )* {ambito--;} '}' { if(!$primero) $s= "{ " + $s + "dependenciesGraphANTLRArbol.scopeExit("+ambito+"); }"; else $s= "{ " + $s + " }";}
    ;
    
 blockStatement returns [String s]
    :   lv=localVariableDeclarationStatement {$s=$lv.s;}
    |   ci=classOrInterfaceDeclaration {$s=$ci.text;}
    |   st=statement[false] {$s=$st.s;}
    ;
    
 localVariableDeclarationStatement returns [String s]
    :    lv=localVariableDeclaration ';'
        {
            $s=$lv.text+";\n";
            for(String var: $lv.vars)
            {
                $s=$s+"dependenciesGraphANTLRArbol.addVariable(\""+var+"\","+ambito+");\n";
            }
        }
    ;
    
localVariableDeclaration returns [Set<String> vars]
    :   variableModifiers type v=variableDeclarators {$vars=$v.vars;}
    ;
    
variableModifiers
    :   variableModifier*
    ;
    
variableModifier
    :   'final'
    |   annotation
    ;

type
  : classOrInterfaceType ('[' ']')*
  | primitiveType ('[' ']')*
  ;

classOrInterfaceType
  : Identifier typeArguments? ('.' Identifier typeArguments? )*
  ;

primitiveType
    :   'boolean'
    |   'char'
    |   'byte'
    |   'short'
    |   'int'
    |   'long'
    |   'float'
    |   'double'
    ;

typeArguments
    :   '<' typeArgument (',' typeArgument)* '>'
    ;
    
typeArgument
    :   type
    |   '?' (('extends' | 'super') type)?
    ;

variableDeclarators returns [Set<String> vars]
    :   v=variableDeclarator {$vars= new HashSet<String>();$vars.add($v.var);} (',' v2=variableDeclarator {$vars.add($v.var);} )*
    ;

variableDeclarator returns [String var]
    :   v=variableDeclaratorId ('=' variableInitializer)? {$var = $v.var;}
    ;

variableDeclaratorId returns [String var]
    :   id=Identifier ('[' ']')* {$var = $id.text;}
    ;

variableInitializer
    :   arrayInitializer
    |   expression
    ;

arrayInitializer
    :   '{' (variableInitializer (',' variableInitializer)* (',')? )? '}'
    ;

// CLASS OR INTERFACE DECLARATION

classOrInterfaceDeclaration
    :   classOrInterfaceModifiers (classDeclaration | interfaceDeclaration)
    ;
    
classOrInterfaceModifiers
    :   classOrInterfaceModifier*
    ;

classOrInterfaceModifier
    :   annotation   // class or interface
    |   'public'     // class or interface
    |   'protected'  // class or interface
    |   'private'    // class or interface
    |   'abstract'   // class or interface
    |   'static'     // class or interface
    |   'final'      // class only -- does not apply to interfaces
    |   'strictfp'   // class or interface
    ;

classDeclaration
    :   normalClassDeclaration
    |   enumDeclaration
    ;
 
enumDeclaration
    :   ENUM Identifier ('implements' typeList)? enumBody
    ;

enumBody
    :   '{' enumConstants? ','? enumBodyDeclarations? '}'
    ;

enumConstants
    :   enumConstant (',' enumConstant)*
    ;
    
enumConstant
    :   annotations? Identifier arguments? classBody?
    ;
    
enumBodyDeclarations
    :   ';' (classBodyDeclaration)*
    ;
    
 
    
normalClassDeclaration
    :   'class' Identifier typeParameters?
        ('extends' type)?
        ('implements' typeList)?
        classBody
    ;
    
typeParameters
    :   '<' typeParameter (',' typeParameter)* '>'
    ;

typeParameter
    :   Identifier ('extends' typeBound)?
    ;
        
typeBound
    :   type ('&' type)*
    ;

typeList
    :   type (',' type)*
    ;
    
classBody
    :   '{' classBodyDeclaration* '}'
    ;

classBodyDeclaration
    :   ';'
    |   'static'? block[false]
    |   modifiers memberDecl
    ;
    
memberDecl
    :   genericMethodOrConstructorDecl
    |   memberDeclaration
    |   'void' Identifier voidMethodDeclaratorRest
    |   Identifier constructorDeclaratorRest
    |   interfaceDeclaration
    |   classDeclaration
    ;
    
memberDeclaration
    :   type (methodDeclaration | fieldDeclaration)
    ;

genericMethodOrConstructorDecl
    :   typeParameters genericMethodOrConstructorRest
    ;
    
genericMethodOrConstructorRest
    :   (type | 'void') Identifier methodDeclaratorRest
    |   Identifier constructorDeclaratorRest
    ;

methodDeclaration
    :   Identifier methodDeclaratorRest
    ;

fieldDeclaration
    :   variableDeclarators ';'
    ;

methodDeclaratorRest
    :   formalParameters ('[' ']')*
        ('throws' qualifiedNameList)?
        (   methodBody
        |   ';'
        )
    ;

constructorDeclaratorRest
    :   formalParameters ('throws' qualifiedNameList)? constructorBody
    ;

methodBody
    :   block[false]
    ;

constructorBody
    :   '{' explicitConstructorInvocation? blockStatement* '}'
    ;

explicitConstructorInvocation
    :   nonWildcardTypeArguments? ('this' | 'super') arguments ';'
    |   primary '.' nonWildcardTypeArguments? 'super' arguments ';'
    ;

enumConstantName
    :   Identifier
    ;

formalParameters
    :   '(' formalParameterDecls? ')'
    ;
    
formalParameterDecls
    :   variableModifiers type formalParameterDeclsRest
    ;
    
formalParameterDeclsRest
    :   variableDeclaratorId (',' formalParameterDecls)?
    |   '...' variableDeclaratorId
    ;
    
interfaceDeclaration
    :   normalInterfaceDeclaration
    |   annotationTypeDeclaration
    ;
    
normalInterfaceDeclaration
    :   'interface' Identifier typeParameters? ('extends' typeList)? interfaceBody
    ;


interfaceBody
    :   '{' interfaceBodyDeclaration* '}'
    ;
    
    
interfaceBodyDeclaration
    :   modifiers interfaceMemberDecl
    |   ';'
    ;

interfaceMemberDecl
    :   interfaceMethodOrFieldDecl
    |   interfaceGenericMethodDecl
    |   'void' Identifier voidInterfaceMethodDeclaratorRest
    |   interfaceDeclaration
    |   classDeclaration
    ;
    
interfaceMethodOrFieldDecl
    :   type Identifier interfaceMethodOrFieldRest
    ;
    
interfaceMethodOrFieldRest
    :   constantDeclaratorsRest ';'
    |   interfaceMethodDeclaratorRest
    ;

voidMethodDeclaratorRest
    :   formalParameters ('throws' qualifiedNameList)?
        (   methodBody
        |   ';'
        )
    ;
    
interfaceMethodDeclaratorRest
    :   formalParameters ('[' ']')* ('throws' qualifiedNameList)? ';'
    ;

constantDeclarator
    :   Identifier constantDeclaratorRest
    ;

constantDeclaratorsRest
    :   constantDeclaratorRest (',' constantDeclarator)*
    ;
    
constantDeclaratorRest
    :   ('[' ']')* '=' variableInitializer
    ;
    
interfaceGenericMethodDecl
    :   typeParameters (type | 'void') Identifier
        interfaceMethodDeclaratorRest
    ;
    
voidInterfaceMethodDeclaratorRest
    :   formalParameters ('throws' qualifiedNameList)? ';'
    ;

modifiers
    :   modifier*
    ;
    
modifier
    :   annotation
    |   'public'
    |   'protected'
    |   'private'
    |   'static'
    |   'abstract'
    |   'final'
    |   'native'
    |   'synchronized'
    |   'transient'
    |   'volatile'
    |   'strictfp'
    ;

qualifiedNameList
    :   qualifiedName (',' qualifiedName)*
    ;

qualifiedName
    :   Identifier ('.' Identifier)*
    ;
    
    
    
 //STATEMENT

statement [boolean trasIf] returns [String s]
    : block[false] {$s=$block.s;}
    |   as=ASSERT e1=expression {$s = $as.text + " "+ $e1.text;}  (':' e2=expression {$s = $s + " : "+$e2.text;} )? ';' {$s+=";\n";}
    |   'if' pe=parExpression st1=statement[true] {$s= "if"+$pe.text+" "+$st1.s;} (options {k=1;}: 'else' st2=statement[true]  {$s= $s+" else "+$st2.s;} )?
    |   'for' '(' fc=forControl ')' st=statement[true] { $s="for("+$fc.text+")\n"+$st.s;}
    |   'while' pe=parExpression st=statement[true] {$s = "while"+$pe.text+$st.s; }
    |   'do' st=statement[true] 'while' pe=parExpression ';' {$s = "do\n"+$st.s +"while "+$pe.text+";\n"; }
    |   'try' b1=block[false] {$s="try "+$b1.s;}
        ( c1=catches 'finally' b2=block[false] {$s=$s+$c1.text+" finally "+$b2.s;}
        | c2=catches {$s+=$c2.text; }
        |   'finally' b3=block[false] {$s = $s+ " finally "+$b3.s;}
        )
    |   'switch' pe=parExpression '{' {ambito++;} sw=switchBlockStatementGroups {ambito--;} '}' {$s="switch "+$pe.text+" { "+ $sw.s +" }\ndependenciesGraphANTLRArbol.scopeExit("+ambito+");\n ";}
    |   'synchronized' pe=parExpression b=block[false] {$s="synchronized "+$pe.text+" "+$b.text;}
    |   'return' {$s="return ";} (e=expression {$s+=$e.text;} )? ';'{ $s+=";\n"; }
    |   'throw' e=expression ';' {$s="throw "+$e.text+";\n";}
    |   'break' {$s="break ";} (id=Identifier {$s+=$id.text;} )? ';' { $s+=";\n"; }
    |   'continue' { $s="continue "; } (id=Identifier {$s+=$id.text;} )? ';' { $s+=";\n"; }
    |   ';' {$s = ";";}
    |   stexp=statementExpression ';' 
        {
            if(!$stexp.cod.isEmpty())
            { 
              if( $trasIf)
                 $s = "{ " + $stexp.text + ';' + $stexp.cod +" }\n";
              else
                 $s = $stexp.text + ';' + $stexp.cod +"\n";
            }
            else
                $s=$stexp.text+";\n";
        }
    |   id=Identifier ':' st=statement[false] { $s = $id.text + ':' + $st.text; }
    ;
 
catches
    :   catchClause (catchClause)*
    ;
    
catchClause
    :   'catch' '(' formalParameter ')' block[false]
    ;

formalParameter
    :   variableModifiers type variableDeclaratorId
    ;

switchBlockStatementGroups returns [String s]
@init{
  $s="";
}
    :   (sw=switchBlockStatementGroup { $s+=$sw.s; } )*
    ;
    
/* The change here (switchLabel -> switchLabel+) technically makes this grammar
   ambiguous; but with appropriately greedy parsing it yields the most
   appropriate AST, one in which each group, except possibly the last one, has
   labels and statements. */
switchBlockStatementGroup returns [String s]
@init{
  $s="";
}
    :   (sl=switchLabel {$s+=$sl.text;})+ (bs=blockStatement {$s+=$bs.s;} )*
    ;
    
switchLabel
    :   'case' constantExpression ':'
    |   'case' enumConstantName ':'
    |   'default' ':'
    ;

forControl
options {k=3;} // be efficient for common case: for (ID ID : ID) ...
    :   enhancedForControl
    |   forInit? ';' expression? ';' forUpdate?
    ;

forInit
    :   localVariableDeclaration
    |   expressionList
    ;
    
enhancedForControl
    :   variableModifiers type Identifier ':' expression
    ;

forUpdate
    :   expressionList
    ;

// EXPRESSIONS

parExpression returns [ Set<String> vars, String cod ]
    :   '(' e=expression ')' {$vars=$e.vars; $cod = $e.cod; }
    ;
    
expressionList returns [Set<String> vars, ArrayList<Set<String>> listaVars]
    :   e1=expression {$vars=$e1.vars; $listaVars= new ArrayList<Set<String>>(); $listaVars.add(new HashSet($e1.vars));  } (',' e2=expression {$vars.addAll($e2.vars); $listaVars.add(new HashSet($e2.vars));  } )*
    ;

statementExpression returns [String cod]
    :   ex=expression {$cod = $ex.cod; }
    ;
    
constantExpression
    :   expression
    ;
    
expression returns [Set<String> vars, String cod ]
    :   c=conditionalExpression {$vars= $c.vars; $cod=$c.cod; } 
    (
    a=assignmentOperator e=expression 
    {
      $cod+=$e.cod; 
      
      if(!$c.vars.isEmpty() && !$e.vars.isEmpty())
      {
        $cod = $cod + "variablesIzqANTLRArbol=new HashSet<String>();\n";
        $cod = $cod + "variablesDerANTLRArbol=new HashSet<String>();\n";
        for(String var: $c.vars)
        {
           if(var.startsWith("$"))
             $cod = $cod + "variablesIzqANTLRArbol.add(\"\\"+var+"\");\n";
           else
             $cod = $cod + "variablesIzqANTLRArbol.add(\""+var+"\");\n";
        }
        for(String var: $e.vars)
        {
           if(var.startsWith("$"))
            $cod = $cod + "variablesDerANTLRArbol.add(\"\\"+var+"\");\n";
           else
            $cod = $cod + "variablesDerANTLRArbol.add(\""+var+"\");\n";
        }
        
        $cod = $cod + "AuxiliaryCodeANTLRArbol.processDependency(variablesIzqANTLRArbol,variablesDerANTLRArbol,dependenciesGraphANTLRArbol,"+$a.mantieneValorActual+","+ambito+");\n";
             
      }
       
    } 
    )? 
    ;

assignmentOperator returns [boolean mantieneValorActual]
@init
{
  $mantieneValorActual=true;
}
    :   '=' {$mantieneValorActual=false;}
    |   '+='
    |   '-='
    |   '*='
    |   '/='
    |   '&='
    |   '|='
    |   '^='
    |   '%='
    |   ('<' '<' '=')=> t1='<' t2='<' t3='=' 
        { $t1.getLine() == $t2.getLine() &&
          $t1.getCharPositionInLine() + 1 == $t2.getCharPositionInLine() && 
          $t2.getLine() == $t3.getLine() && 
          $t2.getCharPositionInLine() + 1 == $t3.getCharPositionInLine() }?
    |   ('>' '>' '>' '=')=> t1='>' t2='>' t3='>' t4='='
        { $t1.getLine() == $t2.getLine() && 
          $t1.getCharPositionInLine() + 1 == $t2.getCharPositionInLine() &&
          $t2.getLine() == $t3.getLine() && 
          $t2.getCharPositionInLine() + 1 == $t3.getCharPositionInLine() &&
          $t3.getLine() == $t4.getLine() && 
          $t3.getCharPositionInLine() + 1 == $t4.getCharPositionInLine() }?
    |   ('>' '>' '=')=> t1='>' t2='>' t3='='
        { $t1.getLine() == $t2.getLine() && 
          $t1.getCharPositionInLine() + 1 == $t2.getCharPositionInLine() && 
          $t2.getLine() == $t3.getLine() && 
          $t2.getCharPositionInLine() + 1 == $t3.getCharPositionInLine() }?
    ;

conditionalExpression returns [Set<String> vars, String cod]
    :   coe=conditionalOrExpression {$vars=$coe.vars;$cod=$coe.cod;} ( '?' e1=expression ':' e2=expression {$vars.addAll($e1.vars);$vars.addAll($e2.vars);$cod+=$e1.cod; $cod+=$e2.cod; } )?
    ;

conditionalOrExpression returns [Set<String> vars, String cod]
    :   ca=conditionalAndExpression { $vars=$ca.vars; $cod=$ca.cod;} ( '||' ca2=conditionalAndExpression { $vars.addAll($ca2.vars); $cod+=$ca2.cod;} )*
    ;

conditionalAndExpression returns [Set<String> vars, String cod]
    :   io=inclusiveOrExpression { $vars=$io.vars; $cod=$io.cod; } ( '&&' io2=inclusiveOrExpression { $vars.addAll($io2.vars);$cod+=$io2.cod; } )*
    ;

inclusiveOrExpression returns [Set<String> vars, String cod]
    :   eo=exclusiveOrExpression { $vars=$eo.vars; $cod=$eo.cod; } ( '|' eo2=exclusiveOrExpression { $vars.addAll($eo2.vars); $cod+=$eo2.cod;} )*
    ;

exclusiveOrExpression returns [Set<String> vars, String cod]
    :   ae=andExpression { $vars=$ae.vars; $cod=$ae.cod;} ( '^' ae2=andExpression { $vars.addAll($ae2.vars);$cod+=$ae2.cod; } )*
    ;

andExpression returns [Set<String> vars, String cod]
    :   ee=equalityExpression { $vars=$ee.vars; $cod=$ee.cod;} ( '&' ee2=equalityExpression { $vars.addAll($ee2.vars);$cod+=$ee2.cod; } )*
    ;

equalityExpression returns [Set<String> vars, String cod]
    :   io=instanceOfExpression { $vars=$io.vars; $cod=$io.cod;} ( ('==' | '!=') io2=instanceOfExpression { $vars=$io2.vars;$cod+=$io2.cod; } )*
    ;

instanceOfExpression returns [Set<String> vars, String cod]
    :   rexpr=relationalExpression ('instanceof' type)? {$vars=$rexpr.vars; $cod=$rexpr.cod;}
    ;

relationalExpression returns [Set<String> vars, String cod]
    :   se1=shiftExpression {$vars=$se1.vars; $cod=$se1.cod;} ( relationalOp se2=shiftExpression {$vars.addAll($se2.vars); $cod+=$se2.cod;} )*
    ;
    
relationalOp
    :   ('<' '=')=> t1='<' t2='=' 
        { $t1.getLine() == $t2.getLine() && 
          $t1.getCharPositionInLine() + 1 == $t2.getCharPositionInLine() }?
    |   ('>' '=')=> t1='>' t2='=' 
        { $t1.getLine() == $t2.getLine() && 
          $t1.getCharPositionInLine() + 1 == $t2.getCharPositionInLine() }?
    |   '<' 
    |   '>' 
    ;

shiftExpression returns [Set<String> vars, String cod]
    :   ae1=additiveExpression {$vars=$ae1.vars; $cod=$ae1.cod;} ( shiftOp ae2=additiveExpression {$vars.addAll($ae2.vars); $cod+=$ae2.cod; } )*
    ;

shiftOp
    :   ('<' '<')=> t1='<' t2='<' 
        { $t1.getLine() == $t2.getLine() && 
          $t1.getCharPositionInLine() + 1 == $t2.getCharPositionInLine() }?
    |   ('>' '>' '>')=> t1='>' t2='>' t3='>' 
        { $t1.getLine() == $t2.getLine() && 
          $t1.getCharPositionInLine() + 1 == $t2.getCharPositionInLine() &&
          $t2.getLine() == $t3.getLine() && 
          $t2.getCharPositionInLine() + 1 == $t3.getCharPositionInLine() }?
    |   ('>' '>')=> t1='>' t2='>'
        { $t1.getLine() == $t2.getLine() && 
          $t1.getCharPositionInLine() + 1 == $t2.getCharPositionInLine() }?
    ;


additiveExpression returns [Set<String> vars, String cod]
    :   me1=multiplicativeExpression {$vars=$me1.vars; $cod=$me1.cod;} ( ('+' | '-') me2=multiplicativeExpression {$vars.addAll($me2.vars); $cod+=$me2.cod;} )*
    ;

multiplicativeExpression returns [Set<String> vars, String cod]
    :   ue1=unaryExpression {$vars=$ue1.vars;$cod=$ue1.cod;} ( ( '*' | '/' | '%' ) ue2=unaryExpression {$vars.addAll($ue2.vars); $cod+=$ue2.cod;} )*
    ;
    
unaryExpression returns [Set<String> vars, String cod]
    :   '+' ue=unaryExpression {$vars=$ue.vars;$cod=$ue.cod;}
    |   '-' ue=unaryExpression {$vars=$ue.vars;$cod=$ue.cod;}
    |   '++' ue=unaryExpression {$vars=$ue.vars;$cod=$ue.cod;}
    |   '--' ue=unaryExpression {$vars=$ue.vars;$cod=$ue.cod;}
    |   uen=unaryExpressionNotPlusMinus {$vars=$uen.vars;$cod=$uen.cod;}
    ;

unaryExpressionNotPlusMinus returns [Set<String> vars, String cod]
    :   '~' ue=unaryExpression {$vars=$ue.vars;$cod=$ue.cod;}
    |   '!' ue=unaryExpression {$vars=$ue.vars;$cod=$ue.cod;}
    |   ce=castExpression {$vars=$ce.vars;$cod=$ce.cod;}
    |   p=primary selector* ('++'|'--')?  {$vars=$p.vars;$cod=$p.cod;}
    ;

castExpression returns [Set<String> vars, String cod]
    :  '(' primitiveType ')' ue=unaryExpression  {$vars=$ue.vars; $cod=$ue.cod;}
    |  '(' (type | expression) ')' uen=unaryExpressionNotPlusMinus {$vars=$uen.vars;$cod=$uen.cod;}
    ;

primary returns [Set<String> vars, String cod]
@init
{
  $vars = new HashSet<String>();
  $cod="";
  String var="";
  String var2="";
  int tipoSufijo=this.TIPO_SUFIJO_OTRO;
  boolean empiezaDolar=false;
  boolean primeraConcatenacion=false;
  int numPuntos=0;
}
    :   pe=parExpression {$vars.addAll($pe.vars);} 
    |   'this' ('.' Identifier)* identifierSuffix?
    |   'usuper' superSuffix
    |   literal
    |   'new' creator
    |   id=Identifier {var=$id.text; if(var.startsWith("$")) empiezaDolar=true;  } ('.' id2=Identifier {   if(empiezaDolar && !primeraConcatenacion){ var2=var+"."+$id2.text;} primeraConcatenacion=true; numPuntos++; } )* (ids=identifierSuffix { tipoSufijo=$ids.tipoSufijo; }   )? 
        {
          if(tipoSufijo==this.TIPO_SUFIJO_ARRAY)
          {
             $vars.addAll($ids.vars);
             $vars.add(var);
             if(empiezaDolar && !var2.equals(""))
                $vars.add(var2);
          } 
          else 
            if(tipoSufijo==this.TIPO_SUFIJO_METODO)
            {
               $vars.addAll($ids.vars);
               $vars.add(var);
              
               if(empiezaDolar && !var2.equals("") && numPuntos>1)
                 $vars.add(var2);
                 
              $cod = $cod + "variablesIzqANTLRArbol=new HashSet<String>();\n";
              $cod = $cod + "variablesDerANTLRArbol=new HashSet<String>();\n";
               if(var.startsWith("$"))
                   $cod = $cod + "variablesIzqANTLRArbol.add(\"\\"+var+"\");\n";
               else
                   $cod = $cod + "variablesIzqANTLRArbol.add(\""+var+"\");\n";
               if(empiezaDolar && !var2.equals("") && numPuntos>1)    
                   $cod = $cod + "variablesIzqANTLRArbol.add(\"\\"+var2+"\");\n";
             
              for(String strvar: $ids.vars)
              {
                 if(strvar.startsWith("$"))
                  $cod = $cod + "variablesDerANTLRArbol.add(\"\\"+strvar+"\");\n";
                 else
                  $cod = $cod + "variablesDerANTLRArbol.add(\""+strvar+"\");\n";
              }
              
              $cod = $cod + "AuxiliaryCodeANTLRArbol.processDependency(variablesIzqANTLRArbol,variablesDerANTLRArbol,dependenciesGraphANTLRArbol,true,"+ambito+");\n";
                     
            }
            else
            {
              $vars.add(var);
               if(empiezaDolar && !var2.equals(""))
                $vars.add(var2);
            } 
           
         }
    |   primitiveType ('[' ']')* '.' 'class'
    |   'void' '.' 'class'
    ;

identifierSuffix returns [Set<String> vars, int tipoSufijo]
@init{
  $vars= new HashSet<String>();
  $tipoSufijo = this.TIPO_SUFIJO_OTRO;
}
    :   ('[' ']')+ '.' 'class'
    |   ('[' e=expression {$vars.addAll($e.vars);} ']')+  {$tipoSufijo = this.TIPO_SUFIJO_ARRAY;} // can also be matched by selector, but do here
    |   arguments {$tipoSufijo = this.TIPO_SUFIJO_METODO;$vars=$arguments.vars;}
    |   '.' 'class'
    |   '.' explicitGenericInvocation
    |   '.' 'this'
    |   '.' 'super' arguments
    |   '.' 'new' innerCreator
    ;

creator
    :   nonWildcardTypeArguments createdName classCreatorRest
    |   createdName (arrayCreatorRest | classCreatorRest)
    ;

createdName
    :   classOrInterfaceType
    |   primitiveType
    ;
    
innerCreator
    :   nonWildcardTypeArguments? Identifier classCreatorRest
    ;

arrayCreatorRest
    :   '['
        (   ']' ('[' ']')* arrayInitializer
        |   expression ']' ( '[' expression ']' )* ('[' ']')*
        )
    ;

classCreatorRest
    :   arguments classBody?
    ;
    
explicitGenericInvocation
    :   nonWildcardTypeArguments Identifier arguments
    ;
    
nonWildcardTypeArguments
    :   '<' typeList '>'
    ;
    
selector
    :   '.' Identifier arguments?
    |   '.' 'this'
    |   '.' 'super' superSuffix
    |   '.' 'new' innerCreator
    |   '[' expression ']'
    ;
    
superSuffix
    :   arguments
    |   '.' Identifier arguments?
    ;

arguments returns [Set<String> vars]
@init
{
  $vars = new HashSet<String>();
}
    :   '(' (exprl=expressionList {$vars.addAll($exprl.vars);} )? ')'
    ;
    
literal 
    :   integerLiteral
    |   FloatingPointLiteral
    |   CharacterLiteral
    |   StringLiteral
    |   booleanLiteral
    |   'null'
    ;

integerLiteral
    :   HexLiteral
    |   OctalLiteral
    |   DecimalLiteral
    ;

booleanLiteral
    :   'true'
    |   'false'
    ;

// ANNOTATIONS

annotations
    :   annotation+
    ;

annotation
    :   '@' annotationName ( '(' ( elementValuePairs | elementValue )? ')' )?
    ;
    
annotationName
    : Identifier ('.' Identifier)*
    ;

elementValuePairs
    :   elementValuePair (',' elementValuePair)*
    ;

elementValuePair
    :   Identifier '=' elementValue
    ;
    
elementValue
    :   conditionalExpression
    |   annotation
    |   elementValueArrayInitializer
    ;
    
elementValueArrayInitializer
    :   '{' (elementValue (',' elementValue)*)? (',')? '}'
    ;
    
annotationTypeDeclaration
    :   '@' 'interface' Identifier annotationTypeBody
    ;
    
annotationTypeBody
    :   '{' (annotationTypeElementDeclaration)* '}'
    ;
    
annotationTypeElementDeclaration
    :   modifiers annotationTypeElementRest
    ;
    
annotationTypeElementRest
    :   type annotationMethodOrConstantRest ';'
    |   normalClassDeclaration ';'?
    |   normalInterfaceDeclaration ';'?
    |   enumDeclaration ';'?
    |   annotationTypeDeclaration ';'?
    ;
    
annotationMethodOrConstantRest
    :   annotationMethodRest
    |   annotationConstantRest
    ;
    
annotationMethodRest
    :   Identifier '(' ')' defaultValue?
    ;
    
annotationConstantRest
    :   variableDeclarators
    ;
    
defaultValue
    :   'default' elementValue
    ; 
    

//LEXER    
    
HexLiteral : '0' ('x'|'X') HexDigit+ IntegerTypeSuffix? ;

DecimalLiteral : ('0' | '1'..'9' '0'..'9'*) IntegerTypeSuffix? ;

OctalLiteral : '0' ('0'..'7')+ IntegerTypeSuffix? ;

fragment
HexDigit : ('0'..'9'|'a'..'f'|'A'..'F') ;

fragment
IntegerTypeSuffix : ('l'|'L') ;

FloatingPointLiteral
    :   ('0'..'9')+ '.' ('0'..'9')* Exponent? FloatTypeSuffix?
    |   '.' ('0'..'9')+ Exponent? FloatTypeSuffix?
    |   ('0'..'9')+ Exponent FloatTypeSuffix?
    |   ('0'..'9')+ FloatTypeSuffix
    ;

fragment
Exponent : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;

fragment
FloatTypeSuffix : ('f'|'F'|'d'|'D') ;

CharacterLiteral
    :   '\'' ( EscapeSequence | ~('\''|'\\') ) '\''
    ;

StringLiteral
    :  '"' ( EscapeSequence | ~('\\'|'"') )* '"'
    ;

fragment
EscapeSequence
    :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\'|'\$')
    |   UnicodeEscape
    |   OctalEscape
    ;

fragment
OctalEscape
    :   '\\' ('0'..'3') ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7')
    ;

fragment
UnicodeEscape
    :   '\\' 'u' HexDigit HexDigit HexDigit HexDigit
    ;

ENUM:   'enum' {if (!enumIsKeyword) $type=Identifier;}
    ;
    
ASSERT
    :   'assert' {if (!assertIsKeyword) $type=Identifier;}
    ;
    
Identifier 
    :   Letter (Letter|JavaIDDigit)*
    ;

/**I found this char range in JavaCC's grammar, but Letter and Digit overlap.
   Still works, but...
 */
fragment
Letter
    :  '\u0024' |
       '\u0041'..'\u005a' |
       '\u005f' |
       '\u0061'..'\u007a' |
       '\u00c0'..'\u00d6' |
       '\u00d8'..'\u00f6' |
       '\u00f8'..'\u00ff' |
       '\u0100'..'\u1fff' |
       '\u3040'..'\u318f' |
       '\u3300'..'\u337f' |
       '\u3400'..'\u3d2d' |
       '\u4e00'..'\u9fff' |
       '\uf900'..'\ufaff'
    ;

fragment
JavaIDDigit
    :  '\u0030'..'\u0039' |
       '\u0660'..'\u0669' |
       '\u06f0'..'\u06f9' |
       '\u0966'..'\u096f' |
       '\u09e6'..'\u09ef' |
       '\u0a66'..'\u0a6f' |
       '\u0ae6'..'\u0aef' |
       '\u0b66'..'\u0b6f' |
       '\u0be7'..'\u0bef' |
       '\u0c66'..'\u0c6f' |
       '\u0ce6'..'\u0cef' |
       '\u0d66'..'\u0d6f' |
       '\u0e50'..'\u0e59' |
       '\u0ed0'..'\u0ed9' |
       '\u1040'..'\u1049'
   ;

WS  :  (' '|'\r'|'\t'|'\u000C'|'\n') {$channel=HIDDEN;}
    ;

COMMENT
    :   '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}
    ;

LINE_COMMENT
    : '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
    ;
